本文へ移動
cccskills
無料GitHub で公開

check-l10n

Run before pushing a PR that touched UI to find untranslated keys in any of the 21 non-English locales and to catch user-visible English strings that bypass context.l10n. Reports findings as a checklist; refuses to declare clean until all are addressed or explicitly waived. Invoke with /check-l10n.

インストール方法を見る

含まれるファイル(2)

  • SKILL.md7.8 KB
  • scan_strings.py10.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Check L10n Skill

Purpose

Catch the two ways localization breaks in this repo before users see English in a non-English build:

  1. Untranslated keys. A key added to app_en.arb but never translated into one of the 21 non-English locales (am, ar, bg, de, es, fil, fr, id, it, ja, ko, ms, nl, pl, pt, ro, sv, tr, ur, vi, zh). mobile/lib/l10n/app_*.arb is the live list.
  2. Hardcoded user-visible English. Strings rendered straight to the user from widget code without going through context.l10n.<key>. These never show up in .arb files because they were never extracted, so no amount of translation work fixes them.

Run this before every PR push that touches mobile/lib/.

This skill checks that a string is translated. Whether it is translated in the locale's voice is a separate question with its own source of truth: mobile/docs/LOCALIZATION_STYLE_GUIDE.md carries the per-locale register table, the Spanish dialect decision, the terms that never get translated, and the review tiers.

Workflow

Step 1: Determine scope

If the user passed paths after /check-l10n, scan those. Otherwise scan the union of staged + unstaged changes vs the working tree.

# Default: changed files
git -C mobile status --porcelain | awk '{print $NF}' | grep '\.dart$'

# Or explicit: argument paths

Step 2: Run the arb consistency test (if present)

cd mobile && flutter test test/l10n/arb_consistency_test.dart

This test compares every .arb file against app_en.arb and fails if any locale is missing keys that aren't on the explicit _knownUntranslatedDebt allow-list.

If the test file does not exist on this branch, skip this step and rely entirely on Step 3 plus the inline check below. Note in the report that arb consistency was not verified.

Inline fallback when the test doesn't exist

If mobile/test/l10n/arb_consistency_test.dart is missing, do the equivalent check by hand:

cd mobile/lib/l10n
python3 - <<'PY'
import json, glob
en = json.load(open('app_en.arb'))
en_keys = {k for k in en if not k.startswith('@') and k != '@@locale'}
for f in sorted(glob.glob('app_*.arb')):
    if f == 'app_en.arb':
        continue
    other = json.load(open(f))
    other_keys = {k for k in other if not k.startswith('@') and k != '@@locale'}
    missing = en_keys - other_keys
    if missing:
        print(f"{f}: {len(missing)} missing key(s)")
        for k in sorted(missing)[:20]:
            print(f"  - {k}")
PY

Step 3: Scan for hardcoded English in changed files

python3 .claude/skills/check-l10n/scan_strings.py

Or with explicit paths:

python3 .claude/skills/check-l10n/scan_strings.py mobile/lib/screens/auth/foo.dart

The scanner emits one line per candidate, formatted <path>:<line>:<col> [<rule>] '<literal>'. Exit code is 1 when there are findings, 0 otherwise.

The scanner only inspects files under mobile/lib/. It excludes generated files (*.g.dart, *.freezed.dart, *.mocks.dart), the l10n/ directory, and any test/ or integration_test/ tree. It also skips lines inside Log.*(), developer.log(), print(), assert(), throw <Type>Exception(), and route-name constants — those literals are not user-visible.

Step 4: Report as a checklist

Output one section per category. Use the literal output of the underlying tools rather than paraphrasing — the user should be able to copy a path and jump straight to the line.

## Localization check — <branch>

### 1. ARB consistency
- ✅ All 17 locales have every key in app_en.arb
  (or)
- ❌ app_de.arb missing 7 keys: authConfirmPasswordLabel, ...
  (or)
- ⚠️  arb_consistency_test.dart not present on this branch — used inline
     fallback. Verify before merge.

### 2. Hardcoded English in changed UI files
- ✅ No likely user-visible English literals found.
  (or)
- ❌ 5 candidate(s):
  mobile/lib/screens/auth/login_options_screen.dart
    L147:26  [Text-literal]  'Amber app is not installed'
    L316:27  [label-arg]  'Sign in'
  ...

End with one of:

  • ✅ OK to push — both checks passed.
  • ❌ Do not push — list the actions required.
  • ⚠️ Push with caveat — only after the user explicitly waives a finding, documenting why in the report.

Fixing findings

Untranslated keys

If the missing locale is one we ship to native speakers (the user can confirm the current launch list), translate. Otherwise, add the key to the _knownUntranslatedDebt set in mobile/test/l10n/arb_consistency_test.dart with a comment naming which locales still need a pass. Don't expand the debt set silently — it should always be reviewable as "the list of stuff that isn't translated yet, on purpose".

Hardcoded English

Each finding has three resolutions, in order of preference:

  1. Add an l10n key to mobile/lib/l10n/app_en.arb, then route the widget through context.l10n.<key>. If the value already exists under a slightly different name, reuse it instead of creating a duplicate.
  2. Mark as not user-visible. If the literal really isn't reaching the user (e.g., a debug-only widget, a developer-only flag, semantic test identifier), consider whether the scanner needs an additional skip line pattern. A skip rule should be earned by at least 3 distinct examples; one-offs aren't worth the regex maintenance.
  3. Waive with reason. Brand strings and protocol tokens that should NEVER be translated ("Divine", "Nostr", npub, nsec, bunker://) are legitimate hardcoded English — the full locked list is in the localization style guide. Note the waiver in the PR description rather than silencing the scanner — future readers should be able to see why this finding was accepted.

Common rule meanings

RuleCatches
Text-literalText('Foo') and const Text("Bar")
AppBar-title-Texttitle: Text('Foo') (specialization of Text-literal)
label-arglabel: 'Foo' named param to any widget
title-argtitle: 'Foo' named param to any widget
hintText-arghintText: 'Foo' (form fields)
helperText-arghelperText: 'Foo' (form fields)
tooltip-arg / Tooltip-messagetooltip text
semanticLabel-arg / semanticsLabel-argaccessibility labels
user-message-callfirst positional arg of a method whose name includes Error/Message/Snackbar/Toast/Dialog/Banner/Notification

Limitations

  • The scanner is regex-based and will miss heavily templated code (string builders, .padLeft(...), '$prefix - $suffix' constructions). Treat a clean run as "no obvious leaks", not "all leaks ruled out".
  • Brand strings ("Divine", "OpenVine", "Vine") will sometimes trip the user-visible heuristic. Waive them in the PR description rather than trying to silence them in the scanner.
  • The scanner only flags strings starting with a capital letter and containing a space — purely lowercase or single-word UI copy ("ok", "submit") will not be caught. This is a deliberate trade-off for signal-to-noise; manual review remains necessary for short labels.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y". Use when: (1) ExternalSecret shows OutOfSync in ArgoCD but won't sync, (2) ArgoCD application status shows "namespace X is not permitted in project 'infrastructure'", (3) ExternalSecret targets a namespace managed by a different ArgoCD project, (4) Using apps-of-apps pattern with separate infrastructure and application projects.

日本語の概要は準備中です。原文の説明を表示しています。

divinevideo/divine-mobile2662026年10月13日 更新

Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language. After selection, generates AI image prompts and visual briefs section-by-section. Use when the user shares content and needs visual direction, image sourcing, or creative direction for any material.

日本語の概要は準備中です。原文の説明を表示しています。

divinevideo/divine-mobile2662026年10月13日 更新

Fix "Null check operator used on a null value" errors when an object is set to null during an async await. Use when: (1) Object reference is nullified while awaiting, (2) Code accesses object with ! after await returns, (3) Cancel/dispose operations run concurrently with async operations on same object. Solution: capture local reference before await.

日本語の概要は準備中です。原文の説明を表示しています。

divinevideo/divine-mobile2662026年10月13日 更新

Add custom metadata headers (x-amz-meta-*) to AWS v4 signed requests for GCS S3-compatible API. Use when: (1) Adding custom metadata to GCS uploads via S3 API, (2) Getting signature mismatch errors after adding new headers, (3) x-amz-meta-* headers being ignored or causing 403 errors. Custom headers MUST be included in canonical headers and signed headers list.

日本語の概要は準備中です。原文の説明を表示しています。

divinevideo/divine-mobile2662026年10月13日 更新

Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings. Use when: (1) Password authentication fails with correct password, (2) Secret created with `<<< "value"` syntax, (3) Error like "password authentication failed" or "invalid token" despite correct value. Bash here-strings (`<<<`) add a trailing newline that corrupts secrets.

日本語の概要は準備中です。原文の説明を表示しています。

divinevideo/divine-mobile2662026年10月13日 更新

Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp). Use when: (1) Media moderation, transcoding, or analysis silently skips files from Blossom or content-addressed storage servers, (2) URL extraction from Nostr event tags (imeta, r tags) drops URLs without recognized extensions, (3) CDN fallback URLs append .mp4 but the actual server uses extensionless content-addressed paths like /{sha256}. Common in Nostr video events (kind 34236) where different clients use different URL formats.

日本語の概要は準備中です。原文の説明を表示しています。

divinevideo/divine-mobile2662026年10月13日 更新

divinevideo のスキルをすべて見る

このスキルの問題を報告する