Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Myanmar Tools Laravel Package

googlei18n/myanmar-tools

A PHP library from Google i18n for working with Myanmar text. It detects Zawgyi versus Unicode, converts between the two encodings, and normalizes Burmese text for reliable display, search, and sorting. Useful for cleaning user input and migrating legacy Zawgyi data.

Deep Wiki
Context7

Getting Started

googlei18n/myanmar-tools solves one very specific problem: Myanmar text can be encoded as Zawgyi-One (a legacy, font-dependent encoding still common in Myanmar) or Unicode (the correct standard). The two look similar but are byte-wise incompatible, so mixing them in a database, search index, or API silently breaks sorting, search, and rendering. This package gives you two things:

  1. A detector that returns the probability a string is Zawgyi (it is a statistical model, not a regex).
  2. A converter that transliterates Zawgyi → Unicode (and Unicode → Zawgyi).

For a Laravel app, you almost always want the detector on input and the converter only when confidence is high.

Install

The repo is a multi-language monorepo; the PHP implementation lives under php/. Check whether your Composer resolution works first:

composer require googlei18n/myanmar-tools

If it is not on Packagist for your version constraints (the last tagged release is from 2020), add it as a VCS or path repository rather than copying files ad hoc:

{
  "repositories": [
    { "type": "vcs", "url": "https://github.com/googlei18n/myanmar-tools" }
  ]
}

Then vendor the PHP source into a stable namespace you control:

mkdir -p app/Support/MyanmarTools
# copy php/src/*.php and the model .dat file into this directory

The detector loads a trained model file (zawgyiUnicodeModel.dat). Whatever install path you choose, verify the model file is shipped and resolvable at runtime — this is the #1 cause of “it works locally, fails in production.”

First use case: normalize a profile/bio field

The highest-value first integration is a single write path, e.g. a user's display name or bio:

namespace App\Services;

use MyanmarTools\ZawgyiConverter;
use MyanmarTools\ZawgyiDetector;

class MyanmarText
{
    public function __construct(
        private ZawgyiDetector $detector,
        private ZawgyiConverter $converter,
    ) {}

    public function isZawgyi(string $text): bool
    {
        return $this->detector->detectZawgyiProbability($text) > 0.95;
    }

    public function toUnicode(string $text): string
    {
        return $this->isZawgyi($text)
            ? $this->converter->zawgyiToUnicode($text)
            : $text;
    }
}

Bind it as a singleton in a service provider (the detector is stateful/expensive to construct):

$this->app->singleton(MyanmarText::class);

Then in a FormRequest:

protected function prepareForValidation(): void
{
    if ($this->filled('bio')) {
        $this->merge([
            'bio' => app(MyanmarText::class)->toUnicode($this->input('bio')),
        ]);
    }
}

Where to look first in the repo: php/ for ZawgyiDetector, ZawgyiConverter, and the model file; the README.md for the language-specific API names, which differ slightly between the C++, Java, JS, and PHP ports.

Implementation Patterns

Wrap the library behind an interface

Even if you never swap implementations, wrapping it lets you fake it in tests and change thresholds in one place:

interface EncodingNormalizer
{
    public function detect(string $text): float;
    public function normalize(string $text): string;
}

Expose thresholds in config/myanmar.php:

return [
    'zawgyi_threshold' => env('MYANMAR_ZAWGYI_THRESHOLD', 0.95),
    'unicode_threshold' => env('MYANMAR_UNICODE_THRESHOLD', 0.05),
];

Treat the range between those as “unknown” and do not auto-convert — see Gotchas.

Input normalization middleware

For apps with many write endpoints, a middleware that normalizes a whitelist of fields is cleaner than touching every controller:

public function handle($request, Closure $next)
{
    $fields = config('myanmar.normalize_fields', ['name', 'bio', 'address']);

    foreach ($fields as $field) {
        if (is_string($request->input($field))) {
            $request->merge([
                $field => app(MyanmarText::class)->toUnicode($request->input($field)),
            ]);
        }
    }

    return $next($request);
}

Keep it opt-in per route group; do not globally rewrite every string in every request.

Store the original alongside the normalized value

Auto-conversion is a judgement call, and users may disagree. Persist both:

$table->text('bio');
$table->text('bio_original')->nullable();
$table->string('bio_encoding', 16)->nullable(); // unicode|zawgyi|unknown

This gives you an audit trail, a rollback path, and data to tune thresholds against later.

Eloquent cast / mutator

For model-level normalization, a custom cast keeps controllers thin:

class MyanmarTextCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes) { return $value; }

    public function set($model, $key, $value, $attributes)
    {
        return [$key => app(MyanmarText::class)->toUnicode((string) $value)];
    }
}

Prefer this for content fields you own; avoid silently mutating fields where the raw bytes matter (e.g. imported legacy data).

Backfill existing data with a queued command

The detector is not cheap per row. Chunk aggressively and push conversion into jobs:

User::whereNull('bio_normalized')
    ->chunkById(500, function ($users) {
        foreach ($users as $user) {
            NormalizeUserBio::dispatch($user->id);
        }
    });

Inside the job, re-fetch the model, detect, update bio, set bio_encoding, and log anything in the grey zone instead of converting.

Search and filtering

Detection/conversion matters most for search. Normalize both sides:

  • Convert user input queries through the same MyanmarText service before hitting the index.
  • Reindex converted content so Zawgyi and Unicode users find the same records.
  • If you use Scout/Meilisearch/Algolia, put the normalization in the searchable array, not in the query builder.

Rendering and APIs

If you cannot convert legacy content immediately, you still need to render it. Store the detected encoding and emit it to the frontend so you can switch font-family per record:

'font' => $encoding === 'zawgyi' ? 'Zawgyi-One' : 'Noto Sans Myanmar',

For APIs, return the encoding hint as metadata rather than converting responses on the fly; clients can decide how to render.

Frontend alternative

The JS port (npm) is useful when you want to detect in the browser before submitting — good UX for showing “this looks like Zawgyi; convert it?” and reducing server-side rewrite surprises. Keep the server-side conversion as the source of truth.

Gotchas and Tips

  • Detection is probabilistic, and short strings are unreliable. Names, single words, and 1–2 syllable strings routinely produce misleading probabilities. Require a minimum length (e.g. ≥ 20 characters) before auto-converting; otherwise store as-is and flag.
  • Respect the grey zone. The README recommends thresholds; treat anything between them as unknown. Blindly converting at > 0.5 will corrupt legitimate Unicode text, which is worse than leaving Zawgyi in place.
  • Conversion is not a safe round-trip. zawgyiToUnicode then unicodeToZawgyi (or vice versa) can change text. Never use it as a normalizer on already-Unicode content, and don't run the pipeline twice.
  • The model file is a runtime dependency. Ship zawgyiUnicodeModel.dat with the deployment, resolve its path with base_path() or a config value, and add a smoke test that instantiates the detector in CI. Paths that depend on the current working directory break under queue workers and Octane.
  • Construct the detector once. Instantiation and model loading are expensive. Bind it as a singleton; avoid new ZawgyiDetector() inside loops or Blade templates.
  • Unicode normalization is separate. This library handles encoding, not canonical ordering or NFC/NFD. Pair it with Normalizer::normalize($text, Normalizer::FORM_C) (ext-intl) if you also deal with combining marks and ordering issues.
  • The library does not fix fonts. It converts encodings; it does not embed fonts or change CSS. Zawgyi text still requires a Zawgyi font to render correctly, and Unicode text requires a Unicode Myanmar font. Conversion solves rendering only if the font on the other side matches.
  • Mixed-encoding strings exist. Imported content often contains both. The detector returns one probability for the whole string; consider splitting on punctuation/whitespace and detecting per segment for high-value content.
  • The package is quiet since 2020. Verify PHP 8.1+/8.2+ compatibility (typed properties, deprecations) and pin the commit you vendor. Add a test suite around detection results so an upgrade can't silently change behavior.
  • License: NOASSERTION in the metadata. Confirm the actual license terms in the repo before redistributing or embedding the model file in a commercial product.
  • Testing with Myanmar text is awkward. Editors and copy-paste can mangle Zawgyi bytes. Use \u{...} escapes or fixture files loaded with file_get_contents, and assert on probabilities with tolerances rather than exact floats.
  • Extension point: keep the interface boundary. Because ports exist in JS/Ruby/Java, a clean EncodingNormalizer boundary lets you move detection to the frontend or swap the model later without touching controllers.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings